package com.integrance.budgetapp.dao.hibernate;

import java.io.Serializable;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.LockMode;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.integrance.budgetapp.dao.GenericDao;

public class GenericDaoHibernateImpl<T, PK extends Serializable> 
		extends HibernateDaoSupport	implements GenericDao<T, PK> {

	private Class<T> persistentClass;	

	public GenericDaoHibernateImpl(Class<T> type) {
		this.persistentClass = type;		
	}

	public PK create(T o) {
		return (PK) getHibernateTemplate().save(o);
		//return (PK) super.getSession().save(o);
	}

	/**
	 * Lookup an entity by ID. If <tt>lock</tt> is {@code true}, then an upgrade lock is done for this lookup. 
	 * Recall that we are talking about a form of pessimistic locking. This is normally
	 * managed by the Hibernate, but some cases may call for specifiying this advanced usage form.
	 * 
	 * @param pk the entity primary key
	 * @param lock wether or not to lock the entity using {@link LockMode#UPGRADE UPGRADE}
	 * @return the entity given by <tt>pk</tt>
	 * @see org.hibernate.LockMode#UPGRADE
	 */
	public T findById(PK pk, boolean lock) {
        T entity;
        if (lock) {
            //entity = (T) super.getSession().load(getPersistentClass(), pk, LockMode.UPGRADE);
            entity = (T) getHibernateTemplate().load(getPersistentClass(), pk, LockMode.UPGRADE);
        } else {
            //entity = (T) super.getSession().load(getPersistentClass(), pk);
        	entity = (T) getHibernateTemplate().load(getPersistentClass(), pk);
        }

        return entity;
    }
	
	/**
	 * Convenience lookup method, the default mode using no <tt>upgrade</tt> lock. This is the equivalent of calling 
	 * {@code findById(pk, true)}. Recall that we are talking about a form of pessimistic locking. This is normally
	 * managed by the Hibernate, but some cases may call for specifiying this advanced usage form.
	 * 
	 * @param pk the primary key of the entity
	 * @return the entity object 
	 * @see LockMode#UPGRADE org.hibernate.LockMode.UPGRADE
	 */
	public T findById(PK pk) {
		return findById(pk, false);
	}
	

	public void delete(T o) {
		//super.getSession().delete(o);
		getHibernateTemplate().delete(o);
	}	
	
	@SuppressWarnings("unchecked")
	public void saveOrUpdate(T o) {
		//super.getSession().saveOrUpdate(o);
		getHibernateTemplate().saveOrUpdate(o);
	}
			
	@SuppressWarnings("unchecked")
    public List<T> findAll() {
        return findByCriteria();
    }
	
	@SuppressWarnings("unchecked")
    public List<T> findByExample(T exampleInstance) {
        return findByCriteria(Example.create(exampleInstance));
    }		
	
	
	/**
     * Use this inside subclasses as a convenience method.
     */
    @SuppressWarnings("unchecked")
    protected List<T> findByCriteria(Criterion... criterion) {
        Criteria crit = getSession().createCriteria(getPersistentClass());
        for (Criterion c : criterion) {
            crit.add(c);
        }
        return crit.list();
   }
	
	public Class<T> getPersistentClass() {
		return persistentClass;
	}		
}
